#include "titanfall2/Context.h"
#include "titanfall2/UserCmd.h"
#include <MinHook.h>
#include <sigscanner/ModuleScanner.h>
#include <stdexcept>
#include "Signatures.h"


typedef bool(__fastcall* ClientmodeCreateMoveFn)(__int64, float, titanfall2::CUserCmd*, unsigned __int8);
typedef HRESULT(__fastcall* PresentFn)(IDXGISwapChain*, UINT, UINT);

ClientmodeCreateMoveFn originalClientmodeCreateMove = nullptr;
PresentFn originalPresent = nullptr;
namespace titanfall2
{
    Context& Context::Get()
    {
        static Context obj;

        return obj;
    }
    bool __fastcall HookedCreateMove(__int64 ThisPtr, float a2, titanfall2::CUserCmd* cmd, unsigned __int8 a4)
    {

        auto Ret = originalClientmodeCreateMove(ThisPtr, a2, cmd, a4);
        static auto& context = Context::Get();
        {
            std::scoped_lock lock(context.createMoveMutex);
            
            if (context.m_onCreateMovePayload.has_value()) {
                context.m_onCreateMovePayload.value()(cmd);
            }
        }
        return Ret;
    }
    HRESULT hPresent(IDXGISwapChain* pSwapChain, uint32_t SyncInterval, uint32_t Flags)
    {
        static auto& context = Context::Get();
        {
            std::scoped_lock lock(context.presentMutex);

            if (context.m_onSwapChainPresent.has_value()) {
                context.m_onSwapChainPresent.value()(pSwapChain);
            }
        }
        return originalPresent(pSwapChain, SyncInterval, Flags);
    }
    Context::Context()
    {
        MH_Initialize();

        signature_scanner::ModuleScanner clientModuleScanner("client.dll");
        signature_scanner::ModuleScanner dx11ModuleScanner("dxgi.dll");
        const auto createMove = clientModuleScanner.FindPattern(signature::createMove);

        auto createMoveHookRes = MH_CreateHook((void*)createMove.value(),
                                 (void*)&HookedCreateMove,
                                 (void**)(&originalClientmodeCreateMove));

        if (createMoveHookRes != MH_STATUS::MH_OK)
            throw std::runtime_error("Failed to hook CreateMove");

        const auto present = dx11ModuleScanner.FindPattern(signature::present);

        auto presentHookRes = MH_CreateHook((void*)present.value(),
                                            (void*)&hPresent,
                                            (void**)(&originalPresent));

        if (presentHookRes != MH_STATUS::MH_OK)
            throw std::runtime_error("Failed to hook Present");

        MH_EnableHook(MH_ALL_HOOKS);
    }
    Context::~Context()
    {
        MH_DisableHook(MH_ALL_HOOKS);
        MH_Uninitialize();
    }

    void Context::SetOnCreateMove(const std::function<void(titanfall2::CUserCmd *)> &func) {
        std::scoped_lock lock(createMoveMutex);

        m_onCreateMovePayload = func;
    }
}
